﻿using Nemerle.Text;
using Nemerle.Utility;
using Nemerle.Data;

using System;
using System.Collections.Generic;
using System.Console;
using System.Linq;

using FsCheck.Fluent;

namespace QCTests
{
  public module TestArrays
  {
    public Run() : void
    {
      _ = DefaultArbitraries.Add.[NArbitraries]();
      
      Spec.ForAny(xs : array[bool] => xs.And() == xs.All(x => x))
        .QuickCheck("And");
        
      Spec.ForAny((xs : array[int], x : int) => xs.Append(x).SequenceEqual(xs.Concat(array[x])))
        .QuickCheck("Append");
        
      Spec.ForAny((xs : array[int], x : int) => {def (ys, zs) = xs.Break(_ <= x); ys.SequenceEqual(xs.GetWhile(_ > x)) && zs.SequenceEqual(xs.DropWhile(_ > x))})
        .QuickCheck("Break");
        
      Spec.ForAny(xs : array[int] => xs.Categorize().ZipWith(
        xs.GroupBy(x => x).MapSeq(_.ToArray()).ToArray(),
        Enumerable.SequenceEqual).And())
        .QuickCheck("Categorize");
        
      Spec.ForAny(xs : array[int*string] => xs.CategorizeBy(Tuples.First).Seconds().ZipWith(
        xs.GroupBy(Tuples.First).MapSeq(_.ToArray()).ToArray(),
        Enumerable.SequenceEqual).And())
        .QuickCheck("CategorizeBy_1");
        
      Spec.ForAny(xs : array[int*string] => xs.CategorizeBy(Tuples.First).Firsts().SequenceEqual(
        xs.CategorizeBy(Tuples.First).Seconds().Map(x => x.Head()[0])))
        .QuickCheck("CategorizeBy_2");
        
      Spec.ForAny(xs : array[option[int]] => xs.CatOptions().SequenceEqual($[e | Some(e) in xs]))
        .QuickCheck("CatOptions");
        
      Spec.ForAny(xss : array[array[int]] => xss.Concatenate().SequenceEqual(xss.SelectMany(xs => xs)))
        .QuickCheck("Concatenate");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.Concatenate(ys).SequenceEqual(xs.Concat(ys)))
        .QuickCheck("Concatenate2");
        
      Spec.ForAny(xs : array[array[int]] => xs.ConcatMap(x => array[x, x]).SequenceEqual(xs.SelectMany(x => array[x, x])))
        .QuickCheck("ConcatMap");
        
      Spec.ForAny((xs : array[int], x : int) => xs.ContainsElem(x) == xs.Any(_ == x))
        .QuickCheck("ContainsElem");
        
      Spec.ForAny(xs : array[int] => xs.Copy().SequenceEqual(xs))
        .QuickCheck("Copy");
      
      Spec.ForAny((xs : array[int], n : int) => xs.DeleteAll(n).SequenceEqual(xs.Filter(_ != n)))
        .QuickCheck("DeleteAll");
        
      Spec.ForAny((xs : array[int*string], n : int) => xs.DeleteAllBy(n, Tuples.First).SequenceEqual(
        xs.Filter(x => x[0] != n)))
        .QuickCheck("DeleteAllBy");
        
      Spec.ForAny((xs : array[int], n : int) => xs.FindIndices(_ == n).Length - xs.DeleteFirst(n).FindIndices(_ == n).Length < 2)
        .QuickCheck("DeleteFirst_1");
        
      Spec.ForAny((xs : array[int], n : int) => xs.Essence().DeleteFirst(n).SequenceEqual(xs.Except(Arrays.Single(n))))
        .QuickCheck("DeleteFirst_2");
        
      Spec.ForAny((xs : array[int], n : int) => xs.DeleteFirstWhen(_ == n).SequenceEqual(xs.DeleteFirst(n)))
        .QuickCheck("DeleteFirstWhen");
        
      Spec.ForAny((xs : array[int], n : int) => xs.DeleteFirstBy(-n, x => -x).SequenceEqual(xs.DeleteFirst(n)))
        .QuickCheck("DeleteFirstBy");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.DeleteFirsts(ys).Distinct().SequenceEqual(xs.Except(ys)))
        .QuickCheck("DeleteFirsts");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.DeleteFirstsBy(ys, x => -x).Distinct().SequenceEqual(xs.Except(ys, SelectEqComparer(x => -x))))
        .QuickCheck("DeleteFirstsBy");
        
      Spec.ForAny((xs : array[int], n : int) => xs.DeleteWhen(_ > n).SequenceEqual(xs.Filter(_ <= n)))
        .QuickCheck("DeleteWhen");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.Diff(ys).Distinct().SequenceEqual(xs.Except(ys)))
        .QuickCheck("Diff");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.DiffBy(ys, x => -x).Distinct().SequenceEqual(xs.Except(ys, SelectEqComparer(x => -x))))
        .QuickCheck("DiffBy");
        
      Spec.ForAny((xs : array[int], n : int) => xs.Drop(n).SequenceEqual(xs.Skip(n)))
        .QuickCheck("Drop");
      
      Spec.ForAny((xs : array[int], n : int) => xs.DropExactly(n).SequenceEqual(xs.Skip(n)))
        .When((xs,n) => n <= xs.Length && n >= 0)
        .QuickCheck("DropExactly");
        
      Spec.ForAny((xs : array[int], n : int) => xs.DropRight(n).SequenceEqual(xs.Reverse().Skip(n).Reverse()))
        .QuickCheck("DropRight");
        
      Spec.ForAny((xs : array[int], n : int) => xs.DropRightExactly(n).SequenceEqual(xs.Reverse().Skip(n).Reverse()))
        .When((xs,n) => n <= xs.Length && n >= 0)
        .QuickCheck("DropRightExactly");
        
      Spec.ForAny((xs : array[int], n : int) => xs.DropWhile(_ > n).SequenceEqual(xs.SkipWhile(_ > n)))
        .QuickCheck("DropWhile"); 
        
      Spec.ForAny((xs : array[int], n : int) => xs.DropRightWhile(_ > n).SequenceEqual(xs.Reverse().SkipWhile(_ > n).Reverse()))
        .QuickCheck("DropRightWhile"); 
        
      Spec.ForAny((xs : array[int], x : int) => x.ElementOf(xs) == xs.Any(_ == x))
        .QuickCheck("ElementOf");
        
      WriteLine(if (Arrays.Empty.[int]().SequenceEqual(array(0) : array[int])) "Empty-Ok, passed 1 test." else "Empty-Falsifiable, after 1 test.");
      
      Spec.ForAny(xs : array[int] => xs.Essence().SequenceEqual(xs.Distinct()))
        .QuickCheck("Essence");
        
      Spec.ForAny(xs : array[int] => xs.EssenceBy(_.ToString()).SequenceEqual(xs.Distinct(SelectEqComparer(_.ToString()))))
        .QuickCheck("EssenceBy");
      
      Spec.ForAny((xs : array[int], x : int) => xs.Exist(_ > x) == xs.Any(_ > x))
        .QuickCheck("Exist");
        
      Spec.ForAny((xs : array[int], x : int) => xs.Filter(_ > x).SequenceEqual(xs.Where(_ > x)))
        .QuickCheck("Filter");
        
      Spec.ForAny(xs : array[string] => xs.FilterI((i, x) => x.Length < i).SequenceEqual(xs.Where((x, i) => x.Length < i)))
        .QuickCheck("FilterI");
        
      def toOption(value, pred) { if (pred(value)) Some(value) else None() }
      
      Spec.ForAny((xs : array[int], x : int) => Option.Equals(xs.Find(_ > x), toOption(Array.Find(xs, _ > x), _ != default(int))))
        .When((_, x) => x > 0)
        .QuickCheck("Find");
        
      Spec.ForAny((xs : array[int], x : int) => xs.Find(_ > x).IsSome == xs.Any(_ > x))
        .And((xs, x) => if (xs.Find(_ > x).IsSome) xs.Find(_ > x).Value > x else true )
        .QuickCheck("Find2");
        
      Spec.ForAny((xs : array[int], x : int) => Option.Equals(xs.FindIndex(_ > x), toOption(Array.FindIndex(xs, _ > x), _ > -1)))
        .QuickCheck("FindIndex");
      
      Spec.ForAny((xs : array[int], x : int) => xs.FindIndices(_ > x).SequenceEqual(xs.Select((e, i) => (e, i)).Where((e, _) => e > x).Select(((_, i)) => i)))
        .QuickCheck("FindIndices");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.Zip(ys).Firsts().SequenceEqual(xs))
        .When((xs, ys) => xs.Length <= ys.Length)
        .QuickCheck("Firsts");
        
      Spec.ForAny((xs : array[char], x : string) => xs.Fold(x, (s, c) => s + c.ToString()) == xs.Aggregate(x, (s, c) => s + c.ToString()))
        .QuickCheck("Fold");
        
      Spec.ForAny((xs : array[char], x : string) => xs.FoldI(x, (i, s, c) => s + c.ToString() + i.ToString()) == 
        xs.Select((x, i) => (x, i)).Aggregate(x, (s, (c, i)) => s + c.ToString() + i.ToString()))
        .QuickCheck("FoldI");
        
      Spec.ForAny((xs : array[char], x : string) => xs.FoldOption(x, (s, c) => Some(s + c.ToString())) == xs.Aggregate(x, (s, c) => s + c.ToString()))
        .QuickCheck("FoldOption_1");
        
      Spec.ForAny(xs : array[bool] => {
          mutable i = 0;
          _ = xs.FoldOption(true, (s, e) => if(e) {i++; Some(s && e)} else None());
          def res = xs.FindIndex(x => !x);
          if (res.IsSome) res.Value == i else true
        })
        .QuickCheck("FoldOption_2");  
        
      Spec.ForAny((xs : array[char], x : string) => xs.FoldWhile(x, (s, c) => s + c.ToString(), _ => true) == xs.Aggregate(x, (s, c) => s + c.ToString()))
        .QuickCheck("FoldWhile_1");
        
      Spec.ForAny(xs : array[bool] => xs.FoldWhile((true, 0), (s, e) => (s[0] && e, s[1]+1), s => s[0])[1] ==
        (xs.GetWhile(x => x).Length))
        .QuickCheck("FoldWhile_2");  
        
      Spec.ForAny((xs : array[char], x : string) => xs.FoldRight(x, (c, s) => s + c.ToString()) == xs.Reverse().Aggregate(x, (s, c) => s + c.ToString()))
        .QuickCheck("FoldRight");
        
      Spec.ForAny((xs : array[char], x : string) => xs.FoldRightI(x, (i, c, s) => s + c.ToString() + i.ToString()) == 
        xs.Select((x, i) => (x, i)).Reverse().Aggregate(x, (s, (c, i)) => s + c.ToString() + i.ToString()))
        .QuickCheck("FoldRightI");
        
      Spec.ForAny((xs : array[int], x : int) => xs.ForAll(_ > x) == xs.All(_ > x))
        .QuickCheck("ForAll");
        
      Spec.ForAny((xs : array[int], n : int) => xs.Get(n).SequenceEqual(xs.Take(n)))
        .QuickCheck("Get");
        
      Spec.ForAny((xs : array[int], n : int) => xs.GetRightExactly(n)
        .SequenceEqual(xs.Reverse().Take(n).Reverse()))
        .When((xs, n) => n <= xs.Length && n >= 0)
        .QuickCheck("GetRightExactly"); 
        
      Spec.ForAny((xs : array[int], n : int) => xs.GetWhile(_ > n).SequenceEqual(xs.TakeWhile(_ > n)))
        .QuickCheck("GetWhile"); 
        
      Spec.ForAny((xs : array[int], n : int) => xs.GetRightWhile(_ > n).SequenceEqual(xs.Reverse().TakeWhile(_ > n).Reverse()))
        .QuickCheck("GetRightWhile");
      
      Spec.ForAny(xs : array[int] => xs.Head() == xs[0])
        .When(xs => xs.Length > 0)
        .QuickCheck("Head");
        
      Spec.ForAny(xs : array[int] => if(xs.Length > 0) xs.HeadOrNone().Value == xs[0] else xs.HeadOrNone().IsNone)
        .QuickCheck("HeadOrNone");
        
      Spec.ForAny((xs : array[int], x : int) => Option.Equals(xs.IndexOf(x), toOption(Array.IndexOf(xs, x), _ > -1)))
        .QuickCheck("IndexOf");
      
      Spec.ForAny((xs : array[int], x : int) => xs.IndicesOf(x).SequenceEqual(xs.FindIndices(_.Equals(x))))
        .QuickCheck("IndicesOf");
        
      Spec.ForAny(xs : array[int] => xs.Init().SequenceEqual(xs.Reverse().Skip(1).Reverse()))
        .QuickCheck("Init");
        
      Spec.ForAny(xs : array[int] => xs.Inits().MapI(x => x).ForAll((i, ys) => ys.SequenceEqual(xs.Take(i))))
        .QuickCheck("Inits");
        
      Spec.ForAny((xs : array[int], x : int) => xs.Sort().Insert(x).SequenceEqual(
        xs.Sort().GetWhile(_ <= x).Append(x).Concatenate(xs.Sort().DropWhile(_ <= x))))
        .QuickCheck("Insert");  
        
      Spec.ForAny((xs : array[int], x : int) => xs.InsertAt(x, 0).SequenceEqual(xs.Prepend(x)))
        .When((xs, _) => xs.Length > 0)
        .QuickCheck("InsertAt_0_1"); 
        
      Spec.ForAny((xs : array[int], x : int) => xs.InsertAt(x, xs.Length).SequenceEqual(xs.Append(x)))
        .When((xs, _) => xs.Length > 0)
        .QuickCheck("InsertAt_0_2"); 
        
      Spec.ForAny((xs : array[int], n : int) => xs.InsertAt(-n, n).SequenceEqual(xs.Take(n).Concat([-n]).Concat(xs.Skip(n))))
        .When((xs, n) => n <= xs.Length && n >= 0)
        .QuickCheck("InsertAt_1"); 
        
      Spec.ForAny((xs : array[int], n : int) => xs.InsertAt(-n, n).SequenceEqual(xs))
        .When((xs, n) => !(n <= xs.Length && n >= 0))
        .QuickCheck("InsertAt_2");
        
      Spec.ForAny((xs : array[string], x : string) => xs.SortBy(_.Length).InsertBy(x, _.Length)
          .SequenceEqual(xs.SortBy(_.Length).GetWhile(y => y.Length <= x.Length).Append(x).Concatenate(xs.SortBy(_.Length).DropWhile(y => y.Length <= x.Length))))
        .QuickCheck("InsertBy");
        
      Spec.ForAny((xs : array[int], n : int) => xs.InsertExactlyAt(-n, n).SequenceEqual(xs.Get(n).Append(-n).Concatenate(xs.Drop(n))))
        .When((xs, n) => n <= xs.Length && n >= 0)
        .QuickCheck("InsertExactlyAt"); 
        
      Spec.ForAny((xs : array[string], s : string) => xs.Map(x => x.ToArray()).Intercalate(s.ToArray())
        .SequenceEqual(string.Join(s, xs)))
        .QuickCheck("Intercalate");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.Intersection(ys).SequenceEqual(xs.Intersect(ys)))
        .QuickCheck("Intersection");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.IntersectionBy(ys, x => -x).SequenceEqual(xs.Intersect(ys, SelectEqComparer(x => -x))))
        .QuickCheck("IntersectionBy");
        
      Spec.ForAny((xs : array[int], x : int) => xs.Intersperse(x)
          .SequenceEqual(array[xs[0]].Concat(xs.PairwiseWith((_, b) => array[x, b]).Concatenate())))
        .When((xs, _) => xs.Length > 1)
        .QuickCheck("Intersperse_1");
        
      Spec.ForAny((x : int, y : int) => array[x].Intersperse(y).SequenceEqual(array[x]))
        .QuickCheck("Intersperse_2");
        
      Spec.ForAny(y : int => array[].Intersperse(y).SequenceEqual(array[]))
        .QuickCheck("Intersperse_3");
        
      Spec.ForAny(xs : array[int] => xs.IntersperseWith(_ - _)
          .SequenceEqual(array[xs[0]].Concat(xs.PairwiseWith((a, b) => array[a - b, b]).Concatenate())))
        .When(xs => xs.Length > 1)
        .QuickCheck("IntersperseWith_1");
        
      Spec.ForAny(x : int => array[x].IntersperseWith(_ - _).SequenceEqual(array[x]))
        .QuickCheck("IntersperseWith_2");
        
      Spec.ForAny(y : int => array[].IntersperseWith((a, b) => (a + b)/y).SequenceEqual(array[]))
        .QuickCheck("IntersperseWith_3");
        
      Spec.ForAny(xs : array[int] => { mutable summ = 0; xs.Iter(x => summ += x); summ == xs.Sum() })
        .QuickCheck("Iter");
      
      Spec.ForAny(xs : array[string] => { def lst = List(); xs.IterI((i, x) => lst.Add(x + i.ToString())); lst.SequenceEqual(xs.Select((x, i) => x + i.ToString()))})
        .QuickCheck("IterI");
        
      Spec.ForAny(xs : array[int] => xs.IsEmpty() == !xs.Any())
        .QuickCheck("IsEmpty");
        
      Spec.ForAny(xs : array[int] => xs.IsNullOrEmpty() == !xs.Any())
        .QuickCheck("IsNullOrEmpty_1");
        
      Spec.ForAny((xs : array[int], n : int) => xs.Get(n).IsPrefixOf(xs) == true)
        .QuickCheck("IsPrefixOf_1");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.IsPrefixOf(xs.Concatenate(ys)) == true)
        .QuickCheck("IsPrefixOf_2");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.IsPrefixOf(ys.Concatenate(xs)) == false)
        .When((xs : array[int], ys : array[int]) => !xs.IsPrefixOf(ys) && ys.Length > 0 && xs.Length > 0)
        .QuickCheck("IsPrefixOf_3");
        
      Spec.ForAny(xs : array[int] => xs.IsPrefixOf(xs) == true)
        .QuickCheck("IsPrefixOf_4");
      
      Spec.ForAny(xs : array[int] => Arrays.Empty().IsPrefixOf(xs) == true)
        .QuickCheck("IsPrefixOf_5");
        
      Spec.ForAny(xs : array[int] => xs.IsPrefixOf(Arrays.Empty()) == false)
        .When(xs => xs.Length > 0)
        .QuickCheck("IsPrefixOf_6");
        
      Spec.ForAny((xs : array[int], n : int) => xs.Drop(n).IsSuffixOf(xs) == true)
        .QuickCheck("IsSuffixOf_1");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.IsSuffixOf(ys.Concatenate(xs)) == true)
        .QuickCheck("IsSuffixOf_2");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.IsSuffixOf(xs.Concatenate(ys)) == false)
        .When((xs : array[int], ys : array[int]) => !xs.IsSuffixOf(ys) && ys.Length > 0 && xs.Length > 0 && !ys.IsSuffixOf(xs))
        .QuickCheck("IsSuffixOf_3");
        
      Spec.ForAny(xs : array[int] => xs.IsSuffixOf(xs) == true)
        .QuickCheck("IsSuffixOf_4");
      
      Spec.ForAny(xs : array[int] => Arrays.Empty().IsSuffixOf(xs) == true)
        .QuickCheck("IsSuffixOf_5");
        
      Spec.ForAny(xs : array[int] => xs.IsSuffixOf(Arrays.Empty()) == false)
        .When(xs => xs.Length > 0)
        .QuickCheck("IsSuffixOf_6");

      Spec.ForAny((xs : array[int], ys : array[int]) => xs.IsInfixOf(ys.Concatenate(xs).Concatenate(ys)) == true)
        .QuickCheck("IsInfixOf_1");
        
      Spec.ForAny(xs : array[int] => Arrays.Empty().IsInfixOf(xs) == true)
        .QuickCheck("IsInfixOf_2");
        
      Spec.ForAny(xs : array[int] => xs.IsInfixOf(Arrays.Empty()) == false)
        .When(xs => xs.Length > 0)
        .QuickCheck("IsInfixOf_3");
        
      Spec.ForAny(xs : array[int] => xs.IsInfixOf(xs) == true)
        .When(xs => xs.Length > 0)
        .QuickCheck("IsInfixOf_4");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.IsInfixOf(ys) == 
        Arrays.Unfold(ys, s => if(s.Any()) Some((s.Tail(), xs.IsPrefixOf(s))) else None()).Or())
        .When((xs, _) => xs.Length > 0)
        .QuickCheck("IsInfixOf_5");
        
      WriteLine(if ((null : array[int]).IsNullOrEmpty()) "IsNullOrEmpty_2-Ok, passed 1 test." else "IsNullOrEmpty_2-Falsifiable, after 1 test.");
      
      Spec.ForAny(xs : array[int] => xs.LastElem() == xs.Last())
        .When(xs => xs.Length > 0)
        .QuickCheck("LastElem");
        
      Spec.ForAny(xs : array[int] => xs.Map(x => -x).SequenceEqual(xs.Select(x => -x)))
        .QuickCheck("Map");
        
      Spec.ForAny(xs : array[string] => { 
        def (acc, ys) = xs.MapAcc(0, (i, x) => (i + 1, i.ToString() + x));
        ys.SequenceEqual(xs.Select((x, i) => i.ToString() + x)) && acc == xs.Length
        })
        .QuickCheck("MapAcc");
        
      Spec.ForAny(xs : array[string] => {
        def (acc, ys) = xs.MapAccRight(xs.Length - 1, (i, x) => (i - 1, i.ToString() + x));
        def (acc2, ys2) = xs.MapAcc(0, (i, x) => (i + 1, i.ToString() + x));
        ys.SequenceEqual(ys2) && (xs.Length + 1 + acc) == acc2
        })
        .QuickCheck("MapAccRight");
      
      def zipf(x, y) { x.ToString() + y };   
      
      Spec.ForAny((xs : array[int], ys : array[string]) => {
        def l = Math.Min(xs.Length, ys.Length);
        def xs' = xs.Take(l).ToArray();
        def ys' = ys.Take(l).ToArray();
        xs'.Map2(ys', zipf).SequenceEqual(xs'.Select((x, i) => zipf(x, ys'[i])))
      })
        .QuickCheck("Map2");
        
      Spec.ForAny(xs : array[string] => xs.MapI((i, x) => i.ToString() + x).SequenceEqual(xs.Select((x, i) => i.ToString() + x)))
        .QuickCheck("MapI");
        
      Spec.ForAny((xs : array[int], x : int) => xs.MapOption(y => if (y <= x) Some(y + 1) else None())
        .SequenceEqual(xs.Filter(_ <= x).Map(_ + 1)))
        .QuickCheck("MapOption");
        
      Spec.ForAny(xs : array[int] => (xs.MaxIndex() == xs.Length - 1) && xs[xs.MaxIndex()] == xs.LastElem())
        .When(xs => xs.Length > 0)
        .QuickCheck("MaxIndex");
        
      Spec.ForAny(xs : array[int] => xs.Maximum() == xs.Max())
        .When(xs => xs.Length > 0)
        .QuickCheck("Maximum");
        
      Spec.ForAny(xs : array[int] => xs.MaximumBy(x => x) == xs.Maximum())
        .When(xs => xs.Length > 0)
        .QuickCheck("MaximumBy_1");
        
      Spec.ForAny(xs : array[string] => xs.MaximumBy(_.Length).Length == xs.Max.[string, int](x => x.Length))
        .When(xs => xs.Length > 0)
        .QuickCheck("MaximumBy_2");
        
      Spec.ForAny(xs : array[int] => xs.MaximumsBy(x => x).Head() == xs.Maximum())
        .When(xs => xs.Length > 0)
        .QuickCheck("MaximumsBy_1");
        
        Spec.ForAny(xs : array[int] => {def ms = xs.MaximumsBy(x => x); if (ms.Length > 1) ms.PairwiseWith(_ == _).And() else true})
        .When(xs => xs.Length > 0)
        .QuickCheck("MaximumsBy_2");
        
      Spec.ForAny((x : int, n : int) => Arrays.Replicate(x, n).MaximumsBy(y => y).Length == n)
        .When((_, n) => n > 0)
        .QuickCheck("MaximumsBy_3");
        
      Spec.ForAny(xs : array[int] => xs.Minimum() == xs.Min())
        .When(xs => xs.Length > 0)
        .QuickCheck("Minimum");
        
      Spec.ForAny(xs : array[int] => xs.MinimumBy(x => x) == xs.Min())
        .When(xs => xs.Length > 0)
        .QuickCheck("MinimumBy_1");
        
      Spec.ForAny(xs : array[string] => xs.MinimumBy(_.Length).Length == xs.Min.[string, int](x => x.Length))
        .When(xs => xs.Length > 0)
        .QuickCheck("MinimumBy_2");
        
      Spec.ForAny(xs : array[int] => xs.MinimumsBy(x => x).Head() == xs.Minimum())
        .When(xs => xs.Length > 0)
        .QuickCheck("MinimumsBy_1");
        
      Spec.ForAny(xs : array[int] => {def ms = xs.MinimumsBy(x => x); if (ms.Length > 1) ms.PairwiseWith(_ == _).And() else true})
        .When(xs => xs.Length > 0)
        .QuickCheck("MinimumsBy_2");
        
      Spec.ForAny((x : int, n : int) => Arrays.Replicate(x, n).MinimumsBy(y => y).Length == n)
        .When((_, n) => n > 0)
        .QuickCheck("MinimumsBy_3");
        
      Spec.ForAny((xs : array[int], i : int) => xs.Nth(i).Value == xs[i])
        .When((xs, i) => i >= 0 && i < xs.Length)
        .QuickCheck("Nth_1");
        
      Spec.ForAny((xs : array[int], i : int) => xs.Nth(i).IsNone)
        .When((xs, i) => i < 0 || i >= xs.Length)
        .QuickCheck("Nth_2");
        
      Spec.ForAny(xs : array[bool] => xs.Or() == xs.Any(x => x))
        .QuickCheck("Or");
        
      def swap((x,y)) { (y,x) };
        
      Spec.ForAny(xs : array[int] => xs.Organize().ZipWith(
        Arrays.Unfold(xs, s => if(s.Length > 0) Some(swap(s.Span(_ == s[0]))) else None()),
        Enumerable.SequenceEqual).And())
        .QuickCheck("Organize");
        
      Spec.ForAny(xs : array[int] => xs.OrganizeBy(x => x).Seconds().ZipWith(xs.Organize(),
        Enumerable.SequenceEqual).And())
        .QuickCheck("OrganizeBy_1");
        
      Spec.ForAny(xs : array[int] => xs.OrganizeBy(x => x).Firsts().SequenceEqual(
        xs.Organize().Map(_.Head())))
        .QuickCheck("OrganizeBy_2");
        
      Spec.ForAny(xs : array[int*string] => xs.OrganizeBy(Tuples.First).Firsts().SequenceEqual(
        xs.Firsts().Organize().Map(_.Head())))
        .QuickCheck("OrganizeBy_3");
      
      Spec.ForAny(xs : array[int] => xs.Pairwise().SequenceEqual(xs.Zip(xs.Tail())))
        .When(xs => xs.Length > 1)
        .QuickCheck("Pairwise_1");
        
      Spec.For(LimSeq.Array01(), xs : array[int] => xs.Pairwise().SequenceEqual(array(0)))
        .QuickCheck("Pairwise_2");
        
      Spec.ForAny(xs : array[int] => xs.PairwiseWith(_ + _).SequenceEqual(xs.ZipWith(xs.Tail(), _ + _)))
        .When(xs => xs.Length > 1)
        .QuickCheck("PairwiseWith_1");
        
      Spec.For(LimSeq.Array01(), xs : array[int] => xs.PairwiseWith(_ + _).SequenceEqual(array(0)))
        .QuickCheck("PairwiseWith_2");
        
      Spec.ForAny((xs : array[int], x : int) => {def (ts, fs) = xs.Partition(_ > x); ts.SequenceEqual(xs.Filter(_ > x)) && fs.SequenceEqual(xs.Filter(_ <= x))})
        .QuickCheck("Partition");
        
      Spec.ForAny((xs : array[int], x : int) => xs.Prepend(x).SequenceEqual(array[x].Concat(xs)))
        .QuickCheck("Prepend");
        
      Spec.ForAny(xs : array[string] => xs.Reduce(_ + _) == xs.Aggregate(_ + _))
        .When(xs => xs.Length > 0)
        .QuickCheck("Reduce");
        
      Spec.ForAny(xs : array[string] => xs.ReduceOrNone(_ + _).Value == xs.Aggregate(_ + _))
        .When(xs => xs.Length > 0)
        .QuickCheck("ReduceOrNone_1");
        
      Spec.For(LimSeq.Array0(), xs : array[int] => xs.ReduceOrNone(_ + _).IsNone)
        .QuickCheck("ReduceOrNone_2"); 
                
      Spec.ForAny(xs : array[int] => xs.ReducePairs(_ + _).SequenceEqual(xs.FilterI((i, _) => i % 2 != 0).ZipWith(xs.FilterI((i, _) => i % 2 == 0), _ + _)))
        .When(xs => xs.Length > 1)
        .QuickCheck("ReducePairs_1");  
        
      Spec.For(LimSeq.Array01(), xs : array[int] => xs.ReducePairs(_ + _).SequenceEqual(array(0)))
        .QuickCheck("ReducePairs_2"); 
      
      Spec.ForAny(xs : array[string] => xs.ReduceRight(_ + _) == xs.Reverse().Aggregate((a, b) => b + a))
        .When(xs => xs.Length > 0)
        .QuickCheck("ReduceRight");
        
      Spec.ForAny((x : string, n : int) => Arrays.Replicate(x, n).SequenceEqual(Enumerable.Repeat(x, n)))
        .When((_, n) => n > 0)
        .QuickCheck("Replicate_1");
        
      Spec.ForAny((x : string, n : int) => Arrays.Replicate(x, n).SequenceEqual(Seq.Empty()))
        .When((_, n) => n <= 0)
        .QuickCheck("Replicate_2");
        
      Spec.ForAny((x : string, n : int) => Arrays.ReplicateResult(() => x, n).SequenceEqual(Arrays.Replicate(x, n)))
        .When((_, n) => n > 0)
        .QuickCheck("ReplicateResult");
        
      Spec.ForAny(xs : array[int] => xs.Rev().Rev().SequenceEqual(xs))
        .QuickCheck("Rev");
        
      Spec.ForAny((xs : array[int], i : int) => xs.Scan("", (s, x) => s + x.ToString())[i] == 
        xs.Take(i).Aggregate("", (s, x) => s + x.ToString()))
        .When((xs, i) => i < xs.Length - 1 && i >= 0)
        .QuickCheck("Scan");
        
      Spec.ForAny((xs : array[int], seed : string) => xs.ScanRight(seed, (x, s) => s + x.ToString())
        .SequenceEqual(xs.Rev().Scan(seed, (s, x) => s + x.ToString()).Rev()))
        .QuickCheck("ScanRight");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.Zip(ys).Seconds().SequenceEqual(ys))
        .When((xs, ys) => xs.Length >= ys.Length)
        .QuickCheck("Seconds");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.SetAdd(ys).SequenceEqual(xs.Union(ys)))
        .QuickCheck("SetAdd");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.SetAddBy(ys, x => -x).SequenceEqual(xs.Union(ys, SelectEqComparer(x => -x))))
        .QuickCheck("SetAddBy");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.SetDiff(ys).SequenceEqual(xs.Except(ys)))
        .QuickCheck("SetDiff");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => xs.SetDiffBy(ys, x => -x).SequenceEqual(xs.Except(ys, SelectEqComparer(x => -x))))
        .QuickCheck("SetDiffBy");
        
      Spec.ForAny(x : string => Arrays.Single(x).SequenceEqual(array[x]))
        .QuickCheck("Single");
        
      Spec.ForAny(xs : array[int] => xs.Sort().SequenceEqual(xs.OrderBy(x => x)))
        .QuickCheck("Sort");
        
      Spec.ForAny(xs : array[int] => xs.Sort(Comparer.Default.Compare).SequenceEqual(xs.OrderBy(x => x, Comparer.Default)))
        .QuickCheck("Sort2");
        
      Spec.ForAny(xs : array[int] => xs.SortBy(x => -x).SequenceEqual(xs.OrderBy(x => -x)))
        .QuickCheck("SortBy");
        
      Spec.ForAny(xs : array[int] => xs.Sort(Comparer.Default).SequenceEqual(xs.OrderBy(x => x, Comparer.Default)))
        .QuickCheck("Sort3");
       
      Spec.ForAny((xs : array[int], x : int) => {def (ys, zs) = xs.Span(_ > x); ys.SequenceEqual(xs.GetWhile(_ > x)) && zs.SequenceEqual(xs.DropWhile(_ > x))})
        .QuickCheck("Span");
        
      Spec.ForAny((xs : array[int], i : int) => {def (ys, zs) = xs.SplitAt(i); ys.SequenceEqual(xs.Get(i)) && zs.SequenceEqual(xs.Drop(i))})
        .QuickCheck("SplitAt");
        
      Spec.ForAny((xs : array[int], i : int) => {def (ys, zs) = xs.SplitExactlyAt(i); ys.SequenceEqual(xs.Get(i)) && zs.SequenceEqual(xs.Drop(i))})
        .When((xs, i) => i >= 0 && i <= xs.Length)
        .QuickCheck("SplitExactlyAt");
        
      Spec.ForAny(xs : array[int] => xs.Tail().SequenceEqual(xs.Skip(1)))
        .When(xs => xs.Length > 0)
        .QuickCheck("Tail");
        
      Spec.ForAny(xs : array[int] => xs.Tails().MapI(x => x).ForAll((i, ys) => ys.SequenceEqual(xs.Skip(i))))
        .QuickCheck("Tails");
        
      Spec.ForAny((xs : array[int], n : int) => xs.GetExactly(n).SequenceEqual(xs.Take(n)))
        .When((xs, n) => xs.Length >= n && n >= 0)
        .QuickCheck("TakeExactly");
        
      Spec.ForAny(xs : array[int] => xs.ToNList().SequenceEqual(xs))
        .QuickCheck("ToNList");
        
      Spec.ForAny(xs : array[int] => xs.ToSeq().SequenceEqual(xs) && !(xs.ToSeq() is array[int]))
        .QuickCheck("ToSeq");
        
      Spec.ForAny((xs : array[int], ys : array[int]) => Arrays.Replicate(xs, ys.Length).Transpose().Transpose()
        .ZipWith(Arrays.Replicate(xs, ys.Length), Enumerable.SequenceEqual).And())
        .QuickCheck("Transpose");
        
      Spec.ForAny(xs : array[int] => Arrays.Unfold(xs.ToSeq().GetEnumerator(), s => if (s.MoveNext()) Some((s, s.Current)) else None())
        .SequenceEqual(xs))
        .QuickCheck("Arrays.Unfold_1");
        
      Spec.ForAny(n : int => Arrays.Unfold(0, s => if (s <= n) Some((s + 1, s)) else None()).SequenceEqual($[0 .. n]))
        .QuickCheck("Arrays.Unfold_2");
        
      Spec.ForAny(xs : array[int] => Arrays.UnfoldRight(xs.ToNList().Rev(), s => if (!s.IsEmpty) Some((s.Tail, s.Head)) else None())
        .SequenceEqual(xs))
        .QuickCheck("Arrays.UnfoldRight_1");
        
      Spec.ForAny(n : int => Arrays.UnfoldRight(0, s => if (s <= n) Some((s + 1, s)) else None()).SequenceEqual($[n, n - 1 .. 0]))
        .QuickCheck("Arrays.UnfoldRight_2");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => xs.Zip(ys).SequenceEqual(xs.Select((x, i) => (x, ys[i]))))
        .When((xs, ys) => xs.Length <= ys.Length)
        .QuickCheck("Zip_1");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => xs.Zip(ys).SequenceEqual(ys.Select((y, i) => (xs[i], y))))
        .When((xs, ys) => xs.Length >= ys.Length)
        .QuickCheck("Zip_2");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => {
        def zip = xs.Zip(ys);
        def len = zip.Length;
        def (xs', ys') = zip.UnZip();
        xs.GetExactly(len).SequenceEqual(xs') && ys.GetExactly(len).SequenceEqual(ys')
      }).QuickCheck("UnZip");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => xs.ZipWith(ys, zipf).SequenceEqual(xs.Select((x, i) => zipf(x, ys[i]))))
        .When((xs, ys) => xs.Length <= ys.Length)
        .QuickCheck("ZipWith_1");
        
      Spec.ForAny((xs : array[int], ys : array[string]) => xs.ZipWith(ys, zipf).SequenceEqual(ys.Select((y, i) => zipf(xs[i], y))))
        .When((xs, ys) => xs.Length >= ys.Length)
        .QuickCheck("ZipWith_2");
    }
  }
}
